#include "game_client.h"
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

#pragma comment(lib,"Dxguid.lib")
#pragma comment(lib,"Dinput8.lib")

// The current system will create standard input devices remote(here), include mouse and keyboard

HashSet DIDevice_list; // store the DirectInputDevice
HashSet DIEffect_list; // store the DirectInputEffect
//LPDIRECTINPUT8 cur_di = NULL;
extern LPDIRECTINPUT8 directInput_cur;
LPDIRECTINPUTDEVICE8 cur_di_device = NULL;
// the following two device is standard, mouse and keyboard
// they need to be initialized when the device is created

LPDIRECTINPUTDEVICE8 p_mouse = NULL;
LPDIRECTINPUTDEVICE8 p_keyboard = NULL;

extern LPDIRECTINPUTDEVICE8 S_Mouse;
extern LPDIRECTINPUTDEVICE8 S_Keyboard;
extern char keys[256];

bool use_mouse = false;
bool use_keyboard = false ;


LPDIRECTINPUTEFFECT cur_df = NULL;

DIDEVICEOBJECTDATA Mouse_e[100]; // local buffer for the device data
DIDEVICEOBJECTDATA Keyboard_e[100]; 

#define GET_DI_DEVICE(msg) \
	int di_device_id = GET_OBJID(msg); \
	cur_di_device = (LPDIRECTINPUT8)(DIDevice_list.GetDataPtr(di_device_id));

// define the struct
struct MyMouse{
	IDirectInputDevice8 * i_mouse;
	// the data buffer
	DIMOUSESTATE m_MouseState;
}R_Mouse;
struct MyKeyboard{
	IDirectInputDevice8 * i_keyboard;
	// the data buffer
	char m_KeyState[256];
}R_Keyboard;

extern HINSTANCE hinstance; // current process instance
extern HWND hWnd; // current main window


HRESULT FakeDIConfigureDevice(){
	// configure device
	//GET_BUFFER_NO_ARGS();
	MessageBoxA(NULL,"not word ConfigureDevice, no callback", "ERROR",MB_OK);
	return DI_OK;
}
HRESULT FakeDICreateDevice(){
	// create device
	Log::log("server Create Device \n");
	// the most important function
	// check the window first
	if(hWnd == NULL){
		// init window with width and height
		MessageBoxA(NULL,"NULL window? in CreateDevice","ERROR",MB_OK);
	}
	if(directInput_cur== NULL){
		HRESULT hr = DirectInput8Create(hinstance,DIRECTINPUT_VERSION,IID_IDirectInput8,(LPVOID*)&directInput_cur,NULL);
		if(hr != DI_OK){
			printf("Create DirectInput8 failed\n");
			Log::log("Create DirectInput8 failed\n");
		}
	}
	// get the parameters
	//GET_BUFFER_NO_ARGS();
	//REFGUID rguid,
      //   LPDIRECTINPUTDEVICE * lplpDirectInputDevice,
       //  LPUNKNOWN pUnkOuter
	int id =cc.read_int();
	//GUID rguid = READ_DATA(GUID);
	LPDIRECTINPUTDEVICE8A m_device = NULL;



	//HRESULT hhr = cur_di->CreateDevice(rguid,&m_device,NULL);
	///if(hhr == DI_OK){
		cur_di_device = m_device;
		DIDevice_list.AddMember(id,(PVOID)m_device);


		//// set hook
		//if(rguid ==  GUID_SysKeyboard){
		//	// set he keyboard hook
		//	//mouseKey = SetWindowsHookEx(WH_KEYBOARD,keyboard_proc,NULL,threadID);
		//	use_keyboard = true;
		//	p_keyboard = m_device;
		//	printf("keyboard device %d\n",m_device);
		//	m_device->SetCooperativeLevel(hWnd,5);
		////}else if( rguid == GUID_SysMouse){
		////	// set the mouse hook
		////	//keyboardKey = SetWindowsHookEx(WH_MOUSE,mouse_proc,NULL,threadID);
		////	use_mouse = true;
		////	p_mouse = m_device;
		////	printf("mosue device %d\n",m_device);
		////	m_device->SetCooperativeLevel(hWnd,6);
		////}
		//else{
		//	Log::log("DirectInput CreateDevice Unknown device type\n");
		//}
	//}


	//DIDevice_list.AddMember(id,bese_device);
	Log::log("DirectInputDevice created!\n");
	return DI_OK;
}
HRESULT FakeDIGetDeviceStatus(){
	// not work for now
	Log::log("server DirectInputDevice GetDeviceStatus called!\n");
	
	return DI_OK;
}
HRESULT FakeDIRunContorlPanel(){
	Log::log("server DirectInput RunControlPanel\n");

	DWORD dwFlags = cc.read_uint();
	if(directInput_cur == NULL){
		Log::log("DirectInput NULL in RunControlPanel\n");
	}
	return directInput_cur->RunControlPanel(hWnd,dwFlags);
}

HRESULT FakeDIDAcquire(){
	// the acquire function
	Log::log("server acquire device\n");
	//int id = READ_DATA(int);
	char tag = cc.read_char();

	LPDIRECTINPUTDEVICE8A cur = NULL;
	cur  = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(obj_id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur == NULL){
		Log::log("DirectInputDevice  not exist!\n");

	}
	HRESULT ht = cur->Acquire();
	
	return ht;
	
}
HRESULT FakeDIDBuildActionMap(){
	// the build action map function 
	Log::log("server build action map\n");

	//int id = GET_OBJID(msg);
	char tag = cc.read_char();
//	DIACTIONFORMAT af = READ_DATA(DIACTIONFORMAT);
//	DWORD dwFlags = READ_DATA(DWORD);


	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A )DIDevice_list.GetDataPtr(id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur == NULL){
		Log::log("DirectInputDevice not exist\n");
	}
	//return cur->BuildActionMap(&af,NULL,dwFlags);
	return DI_OK;
}
HRESULT FakeDIDCreateEffect(){
	Log::log("server create effect\n");

	int id = obj_id;
	//GUID rguid = READ_DATA(GUID);
	//DIEFFECT ef = READ_DATA(DIEFFECT);

	LPDIRECTINPUTDEVICE8A cur = NULL;
	cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	if(cur == NULL){
		Log::log("DirctInputDevice is NULL in CreateEffect\n");
	}
	LPDIRECTINPUTEFFECT ldief = NULL;
	//HRESULT hr = cur->CreateEffect(rguid,&ef,&ldief,NULL);
	DIEffect_list.AddMember(id,ldief);
	return DI_OK;
}
HRESULT FakeDIDCreateEffectObjects(){
	Log::log("should never happen, no callback function define locally!\n");
	return DIERR_NOTINITIALIZED;
}
HRESULT FakeDIDEnumEffects(){
	return DI_OK;
}
HRESULT FakeDIDEscape(){
	return DI_OK;
}
HRESULT FakeDIDGetCapabilities(){
	return DI_OK;
}

HRESULT FakeDIDGetDeviceData(){
	Log::log("server GetDeviceData called!\n");
	LPDIRECTINPUTDEVICE8A cur = NULL;

	int id = obj_id;
	char t = cc.read_char();
	DWORD cbObjectData = sizeof(DIDEVICEOBJECTDATA);

	DWORD pdwInOut_m = (DWORD)cc.read_uint();
	DWORD dwFlags_m = (DWORD)cc.read_uint();
	DWORD pdwInOut_k = (DWORD)cc.read_uint();
	DWORD dwFlags_k = (DWORD)cc.read_uint();

	/*DWORD pdwInOut_m = READ_DATA(DWORD);
	DWORD dwFlags_m = READ_DATA(DWORD);
	DWORD pdwInOut_k = READ_DATA(DWORD);
	DWORD dwFlags_k = READ_DATA(DWORD);*/

	// get the data
	S_Mouse->Acquire();
	Log::log("Want Mouse Data Total: %d\n",pdwInOut_m);
	HRESULT hrr =S_Mouse->GetDeviceData(cbObjectData,Mouse_e,&pdwInOut_m,dwFlags_m);
	if(DI_OK!=hrr){
		//memset((void *)&mouse,0,sizeof(mouse));
		S_Mouse->Acquire();
		Log::log("DirectInput::GetDeviceData() for mouse failed!\n");
		hrr = S_Mouse->GetDeviceData(cbObjectData,Mouse_e,&pdwInOut_m,dwFlags_m);
	}
	dic.begin_transaction();
	dic.write_uint(hrr);

	// get the keyboard data
	S_Keyboard->Acquire();
	Log::log("Want Key Data Total: %d\n",pdwInOut_k);
	hrr =S_Keyboard->GetDeviceData(cbObjectData,Keyboard_e,&pdwInOut_k,dwFlags_k);
	if(DI_OK!=hrr){
		//memset((void *)&mouse,0,sizeof(mouse));
		S_Keyboard->Acquire();
		Log::log("DirectInput::GetDeviceData() for mouse failed!\n");
		hrr= S_Keyboard->GetDeviceData(cbObjectData,Keyboard_e,&pdwInOut_k,dwFlags_k);
		if(hrr !=DI_OK){
			Log::log("DirectInput::GetDeviceData() for mouse failed again!\n");
		}
	}
	dic.write_uint(hrr);
	
	dic.write_uint(pdwInOut_k);
	dic.write_uint(pdwInOut_m);
	Log::log("key events:%d, mouse events:%d\n", pdwInOut_k, pdwInOut_m);
	
	int size = sizeof(HRESULT)+ 2* sizeof(DWORD);
	int i = 0;
	for(i=0; i< pdwInOut_k;i++){
		dic.write_uint(Keyboard_e[i].dwOfs);
		dic.write_uint(Keyboard_e[i].dwData);
		dic.write_uint(Keyboard_e[i].dwTimeStamp);
		dic.write_uint(Keyboard_e[i].dwSequence);
		dic.write_uint(Keyboard_e[i].uAppData);
		size += sizeof(DIDEVICEOBJECTDATA);
	}
	for(i = 0; i< pdwInOut_m;i++){
		dic.write_uint(Mouse_e[i].dwOfs);
		dic.write_uint(Mouse_e[i].dwData);
		dic.write_uint(Mouse_e[i].dwTimeStamp);
		dic.write_uint(Mouse_e[i].dwSequence);
		dic.write_uint(Mouse_e[i].uAppData);
		size += sizeof(DIDEVICEOBJECTDATA);
	}
	Log::log("DirectInput upload input data!\n");
	dic.end_transaction();
	

	return DI_OK;
}

HRESULT FakeDIDGetDeviceInfo(){
	return DI_OK;
}
extern LPDIRECTINPUTDEVICE8 S_Mouse;
extern LPDIRECTINPUTDEVICE8 S_Keyboard;
struct t_pa{
	HRESULT ret;
	DIMOUSESTATE mouse;
}R_MOUSE;
struct t_pp{
	HRESULT ret;
	char keys[256];
}R_KEY;



// serger GetDeviceState function
HRESULT FakeDIDGetDeviceState(){
	Log::log("server DirectInputDevice GetDeviceStatus called!\n");
	LPDIRECTINPUTDEVICE8A cur = NULL;
	
	int id = obj_id;
	char t = cc.read_char();
	Log::log("tag: %c\n",t);
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	if(cur == NULL){
		//printf("GetDeviceState error with device NULL\n");
	}
	DIMOUSESTATE mouse;


	//the received data format will be like, mouse_data_size, keyboard_data_size, mouse_data, keyboard_data_index(s).
	if(DI_OK!=S_Mouse->GetDeviceState(sizeof(DIMOUSESTATE),&mouse)){
		memset((void *)&mouse,0,sizeof(mouse));
		S_Mouse->Acquire();
		S_Mouse->GetDeviceState(sizeof(DIMOUSESTATE),&mouse);
	}
	if(DI_OK!=S_Keyboard->GetDeviceState(sizeof(keys),keys)){
		memset(keys,0,256);
		S_Keyboard->Acquire();
		S_Keyboard->GetDeviceState(sizeof(keys),keys);
	}
	//the received data format will be like, mouse_data_size, keyboard_data_size, mouse_data, keyboard_data_index(s).
	dic.begin_transaction();

	dic.write_uint(sizeof(DIMOUSESTATE));
	dic.write_uint(0);

	//write mouse data
	dic.write_int(mouse.lX);
	
	dic.write_int(mouse.lY);
	dic.write_int(mouse.lZ);
	dic.write_byte_arr((char *)&(mouse.rgbButtons),4);

	int size = sizeof(DWORD)*2 + sizeof(LONG) *3 + 4;
	DWORD cout = 0;
	for(unsigned char  i =0;i<255;i++){
		//printf("i:%d\n",i);
		if(keys[i]!= 0){
			dic.write_uchar(i);
			cout++;
		}
	}
	if(keys[255]!=0){
		dic.write_uchar(255);
		cout++;
	}

	dic.begin_transaction();
	dic.write_uint(sizeof(DIMOUSESTATE));
	dic.write_uint(cout);

	dic.end_transaction();
	
	return DI_OK;
}

HRESULT FakeDIDRunControlPanel(){
	Log::log("server DirectInputDevice RunControlpanel!\n");

	//int id= GET_OBJID(msg);
	char tag = cc.read_char();
	DWORD dwFlags = (DWORD)cc.read_uint();
	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur!=NULL){
		return cur->RunControlPanel(hWnd,dwFlags);
	}else{
		return DIERR_NOTINITIALIZED ;
	}
}
HRESULT FakeDIDSetActionMap(){
	// action map
	Log::log("server DirectInputDevice SetActionMap\n");

	//int id = GET_OBJID(msg);
	char tag = cc.read_char();
	//DIACTIONFORMATA diaf = READ_DATA(DIACTIONFORMATA);
	DWORD dwFlags = cc.read_uint();
	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur == NULL){
		Log::log("DirectInputDevice SetActionMap, Device NULL\n");
	}
	//return cur->SetActionMap(&diaf,NULL,dwFlags);
	return DI_OK;
}
HRESULT FakeDIDCooperativeLevel(){
	// set device cooperative level
	Log::log("DirectInputDevice SetCooperativeLevel \n");
	//int id = GET_OBJID(msg);
	char tag = cc.read_char();
	DWORD dwFlags = (DWORD)cc.read_uint();
	printf("Cooperative Level:%d\n",dwFlags);
	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur == NULL){
		Log::log("DirectInputDevice SetCooperativeLevel, Device NULL\n");

	}
	return cur->SetCooperativeLevel(hWnd,dwFlags);
	//return DI_OK;
}
HRESULT FakeDIDSetDataFormat(){
	// set data format
	Log::log("DirectInputDevice SetDataFormat\n");
	LPCDIDATAFORMAT lpf = NULL;

	int id = obj_id;
	char tag = cc.read_char();
	switch(tag){
	case 'M':
		lpf = &(c_dfDIMouse);
		break;
	case 'K':
		lpf = &(c_dfDIKeyboard);
		break;
	default:
		lpf = NULL;
		break;

	}

	LPDIRECTINPUTDEVICE8A cur = NULL;
	cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	if(cur == NULL){
		Log::log("DirectInputDevice SetDataFormat\n");
	}
	return cur->SetDataFormat(lpf);
}
HRESULT FakeDIDUnacquire(){
	// unacquire function
	Log::log("DirectInputDevice Unacquire\n");

	//int id = GET_OBJID(msg);
	char tag = cc.read_char();
	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	if(cur == NULL){
		Log::log("DrectInputDevice NULL in Unacquire\n");
		
	}
	return cur->Unacquire();
}
//HRESULT FakeCreateWindow(){
//	Log::log("CreateWindow\n");
//	TCHAR szAppName[]= TEXT("HelloWin");
//	TCHAR szClassName[]= TEXT("HelloWinClass");
//	//HWND hwnd;
//
//
//	DWORD dwExStyle =(DWORD)cc.read_uint();
//	DWORD dwStyle =(DWORD)cc.read_uint();
//	int x= cc.read_int();
//	int y = cc.read_int();
//	int nWidth = cc.read_int();
//	int nHeight = cc.read_int();
//	bool ret = init_window(nWidth,nHeight);
//	if(ret){
//		return DI_OK;
//	}
//	else
//		return DI_OK;
//
//}
extern void Setup_input();
HRESULT FakeDIDirectInputCreate(){
	Log::log("server DirectInputCreate called\n");
	printf("server DirectInputCreate called!\n");
	//GET_BUFFER_NO_ARGS();
	/*HRESULT hr = DirectInput8Create(hinstance,DIRECTINPUT_VERSION,
		IID_IDirectInput8,(LPVOID*)&cur_di,NULL);
	if(FAILED(hr)){
		Log::log("DirectInput8Create failed!\n");
		printf("FakeDIDirectInputCreate failed!\n");
	}
	else{

	}
	return hr;*/
	Setup_input();
	return D3D_OK;
}
HRESULT FakeDIDAddRef(){
	Log::log("server DirectInputDevice AddRef called!\n");

	int id = obj_id;
	LPDIRECTINPUTDEVICE8A cur = NULL;
	cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	return cur->AddRef();
}
HRESULT FakeDIDRelease(){
	Log::log("server DirectInputDevice Release called!\n");

	//int id = GET_OBJID(msg);
	char tag = cc.read_char();
	LPDIRECTINPUTDEVICE8A cur = NULL;
	//cur = (LPDIRECTINPUTDEVICE8A)DIDevice_list.GetDataPtr(id);
	return cur->Release();
}
HRESULT FakeDIDSetProperty(){
	Log::log("server DirectInputDevice SetProperty called!\n");
	
	char tag = cc.read_char();


	DIPROPDWORD theProperty;
	theProperty.diph.dwSize = sizeof(theProperty);   
   // theProperty.diph.dwHeaderSize = sizeof(theProperty.diph);   
	theProperty.diph.dwHeaderSize = (DWORD)cc.read_uint();
    //theProperty.diph.dwObj = 0;   
	theProperty.diph.dwObj = (DWORD)cc.read_uint();
  //  theProperty.diph.dwHow = DIPH_DEVICE;   
	theProperty.diph.dwHow = (DWORD)cc.read_uint();
    //theProperty.dwData = 17;   
	//theProperty.dwData = READ_DATA(DWORD);

	LPDIRECTINPUTDEVICE8A cur = NULL;
	switch(tag){
	case 'M':
		cur = S_Mouse;
		break;
	case 'K':
		cur = S_Keyboard;
		break;
	default:
		cur = NULL;
		break;
	}
	return cur->SetProperty(DIPROP_BUFFERSIZE,&theProperty.diph);
	
}